package com.example.examsystem.service;

import com.example.examsystem.dto.LoginRequest;
import com.example.examsystem.dto.LoginResponse;
import com.example.examsystem.dto.RegisterRequest;
import com.example.examsystem.dto.UserDto;
import com.example.examsystem.exception.BusinessException;
import com.example.examsystem.model.Permission;
import com.example.examsystem.model.Role;
import com.example.examsystem.model.User;
import com.example.examsystem.model.UserRole;
import com.example.examsystem.repository.PermissionRepository;
import com.example.examsystem.repository.RolePermissionRepository;
import com.example.examsystem.repository.RoleRepository;
import com.example.examsystem.repository.UserRepository;
import com.example.examsystem.repository.UserRoleRepository;
import com.example.examsystem.security.JwtTokenProvider;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 认证服务
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AuthService {
    
    private final AuthenticationManager authenticationManager;
    private final UserRepository userRepository;
    private final RoleRepository roleRepository;
    private final PasswordEncoder passwordEncoder;
    private final JwtTokenProvider jwtTokenProvider;
    private final UserRoleRepository userRoleRepository;
    private final RolePermissionRepository rolePermissionRepository;
    private final PermissionRepository permissionRepository;
    private final CaptchaService captchaService;
    
    /**
     * 用户登录
     */
    @Transactional
    public LoginResponse login(LoginRequest loginRequest) {
        log.info("开始登录验证，用户名: {}", loginRequest.getUsername());

        // 验证验证码（如果需要）
        if (loginRequest.getCaptcha() != null && !captchaService.verifyCaptcha(loginRequest.getCaptcha(), "image", loginRequest.getCaptchaKey())) {
            log.info("跳过验证码验证，使用demo模式");
            throw new BusinessException("验证码校验失败");
        }
        
        // 先验证用户是否存在
        User user = userRepository.findByUsername(loginRequest.getUsername())
            .orElseThrow(() -> {
                log.error("用户不存在: {}", loginRequest.getUsername());
                return new BusinessException("用户不存在");
            });
        
        log.info("找到用户: {}, 启用状态: {}, 锁定状态: {}", 
            user.getUsername(), user.getEnabled(), user.getAccountLocked());
        
        // 验证密码
        boolean passwordMatches = passwordEncoder.matches(loginRequest.getPassword(), user.getPassword());
        log.info("密码验证结果: {}", passwordMatches);
        
        if (!passwordMatches) {
            log.error("密码不匹配，用户: {}", loginRequest.getUsername());
            throw new BusinessException("用户名或密码错误");
        }
        
        try {
            Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                    loginRequest.getUsername(),
                    loginRequest.getPassword()
                )
            );
            
            SecurityContextHolder.getContext().setAuthentication(authentication);
            String token = jwtTokenProvider.generateToken(authentication);
            
            // 更新最后登录时间
            user.setLastLoginAt(LocalDateTime.now());
            user.setLoginCount(user.getLoginCount() + 1);
            userRepository.save(user);
            
            log.info("登录成功，用户: {}", loginRequest.getUsername());
            return new LoginResponse(token, convertToDto(user));
            
        } catch (Exception e) {
            log.error("认证失败，用户: {}, 错误: {}", loginRequest.getUsername(), e.getMessage());
            throw e;
        }
    }
    
    /**
     * 用户注册
     */
    @Transactional
    public UserDto register(RegisterRequest registerRequest) {
        if (userRepository.existsByUsername(registerRequest.getUsername())) {
            throw new BusinessException("用户名已存在");
        }
        
        if (userRepository.existsByEmail(registerRequest.getEmail())) {
            throw new BusinessException("邮箱已被使用");
        }
        
        User user = new User();
        user.setUsername(registerRequest.getUsername());
        user.setPassword(passwordEncoder.encode(registerRequest.getPassword()));
        user.setEmail(registerRequest.getEmail());
        user.setFullName(registerRequest.getFullName());
        user.setEnabled(true);
        
        // 默认分配学生角色
        Role studentRole = roleRepository.findByName("STUDENT")
            .orElseThrow(() -> new BusinessException("默认角色不存在"));

        User savedUser = userRepository.save(user);

        // 保存用户角色关联
        UserRole userRole = new UserRole();
        userRole.setUserId(savedUser.getId());
        userRole.setRoleId(studentRole.getId());
        userRoleRepository.save(userRole);

        return convertToDto(savedUser);
    }
    
    /**
     * 获取当前用户信息
     */
    public UserDto getCurrentUser() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String username = authentication.getName();
        
        User user = userRepository.findByUsername(username)
            .orElseThrow(() -> new BusinessException("用户不存在"));
        
        return convertToDto(user);
    }
    
    /**
     * 刷新令牌
     */
    public LoginResponse refreshToken() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String newToken = jwtTokenProvider.generateToken(authentication);
        
        User user = userRepository.findByUsername(authentication.getName())
            .orElseThrow(() -> new BusinessException("用户不存在"));
        
        return new LoginResponse(newToken, convertToDto(user));
    }
    
    /**
     * 用户登出
     */
    public void logout() {
        SecurityContextHolder.clearContext();
    }
    
    /**
     * 转换为DTO
     */
    private UserDto convertToDto(User user) {
        UserDto userDto = new UserDto();
        userDto.setId(user.getId());
        userDto.setUsername(user.getUsername());
        userDto.setEmail(user.getEmail());
        userDto.setFullName(user.getFullName());
        userDto.setPhoneNumber(user.getPhoneNumber());
        userDto.setEnabled(user.getEnabled());
        userDto.setLastLoginAt(user.getLastLoginAt());
        
        Set<Role> roles = getUserRoles(user.getId());
        Set<String> roleNames = roles.stream()
            .map(Role::getName)
            .collect(Collectors.toSet());
        userDto.setRoles(roleNames);
        
        Set<String> permissions = getUserPermissions(user.getId()).stream()
            .map(permission -> permission.getResource() + ":" + permission.getAction())
            .collect(Collectors.toSet());
        userDto.setPermissions(permissions);
        
        return userDto;
    }

    private Set<Role> getUserRoles(Long userId) {
        List<Long> roleIds = userRoleRepository.findRoleIdsByUserId(userId);
        Set<Role> roles = new HashSet<>();
        
        if (!roleIds.isEmpty()) {
            roles.addAll(roleRepository.findAllById(roleIds));
        }
        
        return roles;
    }

    private Set<Permission> getUserPermissions(Long userId) {
        List<Long> roleIds = userRoleRepository.findRoleIdsByUserId(userId);
        if (roleIds.isEmpty()) {
            return new HashSet<>();
        }
        
        List<Long> permissionIds = rolePermissionRepository.findPermissionIdsByRoleIds(roleIds);
        if (permissionIds.isEmpty()) {
            return new HashSet<>();
        }
        
        return new HashSet<>(permissionRepository.findAllById(permissionIds));
    }

}







